﻿using anydata.DataStorage;

using Microsoft.AspNetCore.SignalR.Client;

using MongoDB.Bson;
using Newtonsoft.Json.Linq;

namespace anydata.DataShare
{
    public class KernelHub
    {
        bool isStarted;
        HubConnection connection;
        readonly ILogger _logger;
        readonly DataProvider _data;
        readonly BucketManager _bucket;
        public KernelHub(DataProvider data,
            ILogger logger,
            string kernelAddr)
        {
            _data = data;
            _logger = logger;
            _bucket = new BucketManager();
            connection = new HubConnectionBuilder()
                .WithUrl(kernelAddr)
                .AddNewtonsoftJsonProtocol(option =>
                {
                    option.PayloadSerializerSettings.Setting();
                }).Build();
            connection.ServerTimeout = TimeSpan.FromMilliseconds(Config.KeepTimeout);
            connection.HandshakeTimeout = TimeSpan.FromMilliseconds(Config.KeepTimeout);
            connection.KeepAliveInterval = TimeSpan.FromMilliseconds(Config.KeepAliveInterval);
            connection.Closed += OnClosed;
            ConnectionOnActions();
        }

        public async Task StartAsync()
        {
            isStarted = true;
            try
            {
                await connection.StartAsync();
                var result = await connection.InvokeAsync<OperateResult>("Auth", Config.KernelAuth);
                _logger.LogInformation(result.ToJson());
            }
            catch (Exception ex)
            {
                _ = OnClosed(ex);
            }
        }

        public async Task StopAsync()
        {
            isStarted = false;
            await connection.StopAsync();
        }

        public async Task OnClosed(Exception? ex)
        {
            _logger.LogError("kernel connect error." + ex?.Message);
            if (isStarted)
            {
                await Task.Delay(2 * 1000);
                await StartAsync();
            }
        }

        public async Task PingAsync(CancellationToken stoppingToken)
        {
            try
            {
                if (connection.State == HubConnectionState.Connected)
                {
                    await connection.InvokeAsync<string>("Ping");
                }
            }
            catch
            {
                _logger.LogError("kernel ping error.");
            }
            finally
            {
                await Task.Delay(5000, stoppingToken);
            }
        }

        private void ConnectionOnActions()
        {
            // 磁盘情况
            connection.On<KernelRequestType>("DiskInfo", DiskInfo);
            // 允许指令
            connection.On<KernelRequestType<JToken>>("DbCommand", DbCommand);
            // 对象的操作
            connection.On<KernelRequestType<string>>("ObjectList", ObjectList);
            connection.On<KernelRequestType<string>>("ObjectGet", ObjectGet);
            connection.On<KernelRequestType<RequstArgs>>("ObjectSet", ObjectSet);
            connection.On<KernelRequestType<string>>("ObjectDelete", ObjectDelete);
            // 文件的操作
            connection.On<string, string, long>("BucketShare", BucketShare);
            connection.On<KernelRequestType<BucketItemData>>("BucketOperate", BucketOperate);
            // 物的操作
            connection.On<KernelRequestType<SnapshotArgs>>("ThingSnapshot", ThingSnapshot);
            connection.On<KernelRequestType<DepreciationArgs>>("ThingDepreciation", ThingDepreciation);
            connection.On<KernelRequestType<string>>("ThingPerfect", ThingPerfect);
            connection.On<KernelRequestType<List<string>>>("ThingDestroy", ThingDestroy);
            connection.On<KernelRequestType<DataSourceLoadOptions>>("ThingLoad", ThingLoad);
            // 集合操作
            connection.On<KernelRequestType>("CollectionList", CollectionList);
            connection.On<KernelRequestType<RequstArgs>>("CollectionRemove", CollectionRemove);
            connection.On<KernelRequestType<RequstArgs>>("CollectionInsert", CollectionInsert);
            connection.On<KernelRequestType<RequstArgs>>("CollectionUpdate", CollectionUpdate);
            connection.On<KernelRequestType<RequstArgs>>("CollectionReplace", CollectionReplace);
            connection.On<KernelRequestType<RequstArgs>>("CollectionSetFields", CollectionSetFields);
            connection.On<KernelRequestType<RequstArgs>>("CollectionAggregate", CollectionAggregate);
            connection.On<KernelRequestType<DataSourceLoadOptions>>("CollectionLoad", CollectionLoad);
            connection.On<KernelRequestType<SummaryOptions>>("CollectionSummary", CollectionSummary);
            // 公开操作
            connection.On<KernelRequestType<MallOrderData>>("PublicOrderReplace", PublicReplaceOrder);
            connection.On<KernelRequestType<MallOrderData>>("PublicCreateOrder", PublicCreateOrder);
        }

        record RequstArgs
        {
            public string key;
            public JToken data;
            public JToken match;
            public JToken replace;
            public JToken options;
            public string collName;
            public ObjectSetData setData;
            public CollectSetFields collSet;
            public CollectUpdateData update;
        }

        private Task DiskInfo(KernelRequestType req)
        {
            return AuthExecute(req, async (token) =>
            {
                var info = await _data.DbInfoAsync(token);
                var file = _bucket.DiskInfo(token);
                info.Add("filesSize", file.size);
                info.Add("filesCount", file.fileCount);
                return OperateResult.Success(info.ToObject<UserDataInfo>());
            });
        }

        private Task DbCommand(KernelRequestType<JToken> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return OperateResult.Success(await _data.CommandAsync(token, req.args));
            });
        }

        private Task ThingLoad(KernelRequestType<DataSourceLoadOptions> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.LoadAsync(token, string.Empty, req.args);
            });
        }

        private Task ThingSnapshot(KernelRequestType<SnapshotArgs> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.SnapshotAsync(token, req.args.collName, req.args.dataPeriod);
            });
        }

        private Task ThingDepreciation(KernelRequestType<DepreciationArgs> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.DepreciationAsync(token, req.args);
            });
        }

        private Task ThingPerfect(KernelRequestType<string> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.PerfectAsync(token, req.args);
            });
        }

        private Task ThingDestroy(KernelRequestType<List<string>> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.DestroyAsync(token, req.args);
            });
        }

        private Task CollectionList(KernelRequestType req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.ListAsync(token);
            });
        }

        private Task CollectionLoad(KernelRequestType<DataSourceLoadOptions> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.LoadAsync(token, req.args.collName, req.args);
            });
        }

        private Task CollectionAggregate(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.AggregateAsync(token, req.args.collName, req.args.options);
            });
        }

        private Task CollectionSummary(KernelRequestType<SummaryOptions> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.SummaryAsync(token, req.args);
            });
        }

        private Task CollectionInsert(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.InsertAsync(token, req.args.collName, req.args.data);
            });
        }

        private Task CollectionUpdate(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.UpdateAsync(token, req.args.collName, req.args.update);
            });
        }

        private Task CollectionReplace(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.ReplaceAsync(token, req.args.collName, req.args.replace);
            });
        }

        private Task CollectionSetFields(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.SetFieldsAsync(token, req.args.collName, req.args.collSet);
            });
        }

        private Task CollectionRemove(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.RemoveAsync(token, req.args.collName, req.args.match);
            });
        }

        private Task ObjectList(KernelRequestType<string> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.ListAsync(token, req.args);
            });
        }

        private Task ObjectGet(KernelRequestType<string> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.GetAsync(token, req.args);
            });
        }
        private Task ObjectSet(KernelRequestType<RequstArgs> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.SetAsync(token, req.args.key, req.args.setData);
            });
        }

        private Task ObjectDelete(KernelRequestType<string> req)
        {
            return AuthExecute(req, (token) =>
            {
                return _data.DeleteAsync(token, req.args);
            });
        }

        private async Task BucketShare(string flag, string link, long startIndex)
        {
            if (link.Contains("0"))
            {
                link = link.Substring(link.IndexOf("0") + 1);
            }
            var key = link.Substring(10).FromBase32().ToBase64();
            var result = await _bucket.LoadFileRange(key, startIndex);
            await TryResponse("FileResponse", flag, result);
        }

        private Task BucketOperate(KernelRequestType<BucketItemData> req)
        {
            return AuthExecute(req, (token) =>
            {
                req.args.targetId = req.targetId;
                return _bucket.Execute(token, req.args);
            });
        }

        private async Task AuthExecute(KernelRequestType req, Func<TokenModel, Task<OperateResult>> operate)
        {
            var token = new TokenModel()
            {
                UserId = req.userId,
                BelongId = req.belongId,
            };
            var result = await operate(token).TryExecute();
            if (string.IsNullOrWhiteSpace(req.flag)) return;
            await TryResponse("Response", req.flag, result);
        }

        private async Task TryResponse(string action, string flag, object? data)
        {
            try
            {
                if (connection.State == HubConnectionState.Connected)
                {
                    await connection.InvokeAsync(action, flag, data);
                }
            }
            catch
            {
                _logger.LogError("kernel response error.");
            }
        }

        private Task PublicReplaceOrder(KernelRequestType<MallOrderData> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.ReplaceOrder(token, req.args);

            });
        }



        private Task PublicCreateOrder(KernelRequestType<MallOrderData> req)
        {
            return AuthExecute(req, async (token) =>
            {
                return await _data.CreateOrderAsync(token, req.args);
            });
        }

    }
}
